#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#include <errno.h>

#include "data_link.h"

char control_values[3] = {C_SET, C_UA, C_DISC};

//0 - not started
//1 - started
//3 - ended 
int transmission = 0;

void stuff_buffer(char* buf, int buf_len, char* new_buf, int new_buf_len) {
	int i;
	int nbuf_offset = 0;

	for (i = 0; i < buf_len; i++) {
		if (buf[i] == FLAG || buf[i] == ESC) {
			new_buf[i + nbuf_offset] = ESC;
			new_buf[i + nbuf_offset + 1] = buf[i] ^ 0x20;
			nbuf_offset += 1;
		} else {
			new_buf[i + nbuf_offset] = buf[i];
		}
	}
}

int stuff_len(char* buf, int len) {
	int i;
	int stuff_counter = 0;
	for (i = 0; i < len; i++) {
		if (buf[i] == FLAG || buf[i] == ESC) {
			stuff_counter += 1;
		}
	}
	return stuff_counter;
}

/*
void destuff_buffer(char* buf, char buf_len, char* new_buf, int new_buf_len) {
	int i;
	int nbuf_offset = 0;
	for (i = 0; i < buf_len; i++) {
		if (buf[i] == ESC) {
			if (buf[i + 1] == 0x5e) //FLAG
				new_buf[i + nbuf_offset] = FLAG;
			else if (buf[i + 1] == 0x5d) //ESC
				new_buf[i + nbuf_offset] = ESC;
			nbuf_offset -= 1;
			i += 1;
		} else {
			new_buf[i + nbuf_offset] = buf[i];
		}
	}
}
*/
/*
void destuff_buffer(char* buf, char buf_len, char* new_buf, int new_buf_len) {
	int i;
	int nbuf_offset = 0;
	
	//bzero(new_buf, MAX_FRAME_SIZE);
	memcpy(new_buf, buf, new_buf_len);
	
	//printf("BUF_LEN=======%d\tNEW_BUF_LEN======%d\n", buf_len, new_buf_len);
		
	for (i = 0; i < buf_len; i++) {
		if (buf[i] == ESC) {
			if (buf[i + 1] == 0x5e) //FLAG
				new_buf[i + nbuf_offset] = FLAG;
			else if (buf[i + 1] == 0x5d) //ESC
				new_buf[i + nbuf_offset] = ESC;
			nbuf_offset -= 1;
			i += 1;
		} else {
			new_buf[i + nbuf_offset] = buf[i];
		}
	}
}*/


int destuff_buffer(char* stuffed, char* destuffed, int size)
{
    int i = 0;
    int j = 0;
    while(i < size)
    {
        if(stuffed[i] == ESC)
        {
            if(stuffed[i+1] == 0x5e)
            {
                destuffed[j] = FLAG;
                i += 2;
                j++;
            }
            else if(stuffed[i+1] == 0x5d)
            {
                destuffed[j] = ESC;
                i += 2;
                j++;
            }
        }
        else
        {
            destuffed[j] = stuffed[i];
            j++;
            i++;
        }
    }
   
    return j;
}


int write_frame_header(int fd, char control) {
	int bytes_written = 0;
	char c[1] = { FLAG };
	bytes_written += write(fd, &c, 1);
	printf("%x\n", c[0]);

	c[0] = ADDRESS;
	bytes_written += write(fd, &c, 1);
	printf("%x\n", c[0]);

	c[0] = control;
	bytes_written += write(fd, &c, 1);
	printf("%x\n", c[0]);

	c[0] = ADDRESS ^ control;
	char new_buf[stuff_len(c, 1) + 1];
	stuff_buffer(c, 1, new_buf, stuff_len(c, 1) + 1);
	bytes_written += write(fd, &new_buf, stuff_len(c,1)+1);
	printf("%x\n", c[0]);
	
	return bytes_written - stuff_len(c, 1);
}

void write_cframe(int fd, char control) {
	printf("\n---\nsending %x control frame\n", control);
	write_frame_header(fd, control);

	char c = FLAG;
	write(fd, &c, 1);
	printf("%x\n", c);

	printf("%x control frame sent\n---\n", control);
}

int write_iframe(int fd, char control, char* packet, int p_len) {
	int i;
	int bytes_written = 0;
	printf("\n---\nsending %x information frame\n", control);
	bytes_written += write_frame_header(fd, control);

	char new_buf[stuff_len(packet, p_len) + p_len];
	stuff_buffer(packet, p_len, new_buf, stuff_len(packet, p_len) + p_len);

	bytes_written += write(fd, &new_buf, stuff_len(packet,p_len)+p_len);
	/*for (i = 0; i < stuff_len(packet, p_len) + p_len; i++)
		printf("%s\n", new_buf);*/

	char bcc2[1] = { packet[0] };
	for (i = 1; i < p_len; i++) {
		bcc2[0] = bcc2[0] ^ packet[i];
	}

	int bcc2_stuff_len = stuff_len(bcc2, 1) + 1;
	char bcc2_stuff[bcc2_stuff_len];
	stuff_buffer(bcc2, 1, bcc2_stuff, bcc2_stuff_len);
	bytes_written += write(fd, &bcc2_stuff, bcc2_stuff_len);
	/*for (i = 0; i < bcc2_stuff_len; i++)
		printf("%x\n", bcc2_stuff[i]);*/

	char c[1] = { FLAG };
	bytes_written += write(fd, &c, 1);
	//printf("%x\n", c[0]);

	printf("%x information frame sent\n---\n", control);

	return bytes_written - stuff_len(packet, p_len) - bcc2_stuff_len;
}


int read_frame(int fd, char* new_buf)
{
	char buf[1] = {'1'};
	char frame_buf[MAX_FRAME_SIZE];
	int frame_buf_i = 0;
	//bzero(frame_buf,MAX_FRAME_SIZE);
	
	while(buf[0]!=FLAG)
	{
		read(fd,&buf,1);
	}
	//printf("read_frame(): flag read: 0x%x\n", buf);
	//recebi uma flag; comecar frame
	buf[0] = '1';
	printf("frame:\n");
	while(1)
	{
		//printf("\nread_frame(): read return = %d, errno = %d\n", read(fd,&buf,1), errno);
		if(buf[0]==FLAG) //final de frame
		{
			break;
		}
		else
		{
			read(fd,&buf,1);
			if(buf[0]==FLAG)
				break;
			else
			{
				frame_buf[frame_buf_i] = buf[0];
				frame_buf_i++;
				printf("%x|",frame_buf[frame_buf_i]);
			}
		}
		//printf("%c|",frame_buf);
		
	}
	usleep(1000*50);
	
	//apply destuff
	int new_len = frame_buf_i-stuff_len(frame_buf, frame_buf_i);
	destuff_buffer(frame_buf, new_buf, frame_buf_i);
	int i;
	printf("\n\ndfsdjkfh\n");
	for(i=0;i<new_len;i++)
		printf("%x|",new_buf[i]);
	printf("\n");
	return new_len;
}

char check_frame_header(char* frame)
{
	if(frame[0] != ADDRESS)
		return 0xFA;
	if(frame[2] == BCC(frame[1]))
		return frame[1];
	return 0xFA;
}

int check_packet_integrity(char* pack, int len_p, char bcc2)
{
	char checksum[1] = { pack[0] };
	int i;
	for(i=1; i<len_p; i++)
	{
		checksum[0] = checksum[0] ^ pack[i];
	}
	printf("checksum = %s; bcc2 = %x\n", checksum, bcc2);
	if(checksum[0] == bcc2)
		return 0; //bcc OK
	printf("bcc2 not ok\n");
	return -1; // bcc NOT OK
}

int extract_data_packet(char* pack, int len_p, char* data)
{
	int i;
	for(i=0;i<len_p;i++)
	{
		data[i] = pack[i+4];
	}
	return i;
}

int extract_packet(char* frame, int len_f, char* packet, int len_p)
{
	int i;
	for(i=0;i<len_p+2;i++)
	{
		//printf("ff: %x, %c\n", frame[i+4],frame[i+4]);
		packet[i] = frame[i+3];
		//printf("pp: %c, %c\n", packet[i],frame[i+3]);
	}
	//TODO
	//FIXME
	//ETC
	//VERIFICAR ESTA CENA
	
	return 0;//check_packet_integrity(packet, len_p, frame[len_f]);
}

int extract_cpacket(char* packet, int p_len, char* filename)
{
	int i;
	for(i=0; i<packet[8];i++)
	{
		filename[i] = packet[8+i];
	}
	return i;
}

int read_iframe(int fd, char *buf)
{
	char frame[MAX_FRAME_SIZE];
	//bzero(frame, MAX_FRAME_SIZE);
	int frame_l = read_frame(fd,frame);
	//printf("read frame\n");
	if (check_frame_header(buf)!= 0xFA) //header OK
	{
		//printf("frame: %s\n", frame);
		//printf("packet header ok\n");
		int i;/*
		printf("FRAME!!\n");
		for(i=0;i<frame_l;i++)
		{
			printf("%c|",frame[i]);
		}*/
		int packet_l = frame_l-4;
		printf("frame_l: %d; packet_l: %d\n", frame_l, packet_l);
		if(packet_l != -1) //SIGNIFICA QUE SE TRATA DE UMA TRAMA INFORMACAO
		{
			char packet[packet_l];
			//printf("info frame\n");
			if (extract_packet(frame,frame_l,packet,packet_l) == 0) //extraido e checksumado
			{
				int i;
				printf("packet info:\n");
				for(i=0;i<=packet_l;i++)
					printf("%c|",packet[i]);
				//printf("packet checksum OK\n");
				printf("\nPACKET[0]: %x, %c\n", packet[0], packet[0]);
				if(packet[0] == 1 || packet[0] == 3) //PACOTE DE CONTROLO
				{
					printf("received control packet\n");
					transmission = packet[0];
					if (transmission == 3)
						return 0; //ending transmission
					return extract_cpacket(packet, packet_l, buf);
				}
				else //PACOTE DE DADOS
				{
					printf("received data_packet\n");
					return extract_data_packet(packet, packet_l-3, buf);
				}
			}
		}
	}
	return -1;
}


